package com.xyz.rok.tools.utils;

import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import org.apache.commons.lang3.StringUtils;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import sun.misc.BASE64Decoder;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author ZhangYiXin
 * @version 1.0
 * @date 2021/4/14 15:53
 **/
@Slf4j
public class OCRUtil {

    private static final int[] whiteRgbRange = {110, 255, 150, 255, 150, 255};
    private static final int[] blackRgbRange = {0, 155, 0, 155, 0, 155};
    private static final int[] blueRgbRange = {60, 99, 160, 195, 200, 222};

    static {
        nu.pattern.OpenCV.loadShared();
    }

    public static String ocrImage(int[] rgbRange, String filePath, String dataPath) {
        String tmpFilePath = imageHandle(rgbRange, new File(filePath));
        String result = null;
        Tesseract tesseract = new Tesseract();
        tesseract.setTessVariable("user_defined_dpi", "300");
        tesseract.setDatapath(dataPath);
        tesseract.setLanguage("eng");
        try {
            File tempFile = new File(tmpFilePath);
            result = tesseract.doOCR(tempFile);
            log.info("识别图片结果:" + result);
            tempFile.delete();
        } catch (TesseractException e) {
            log.error("识别图片错误", e);
        }
        return result;
    }

    public static int[][] getImageGRB(String filePath) {
        File file = new File(filePath);
        int[][] result = null;
        if (!file.exists()) {
            return result;
        }
        Set<String> colorSet = new HashSet<>();
        try {
            BufferedImage bufImg = ImageIO.read(file);
            int height = bufImg.getHeight();
            int width = bufImg.getWidth();
            result = new int[width][height];
            for (int i = 0; i < width; i++) {
                for (int j = 0; j < height; j++) {
                    Color color = new Color(bufImg.getRGB(i, j));
                    colorSet.add(color.getRed() + "," + color.getGreen() + "," + color.getBlue());

                }
            }
            System.out.println(colorSet);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return result;
    }

    public static List<Integer> getUnionMemberCount(String ocrResult) throws Exception {
        List<Integer> unionMemberCount = new ArrayList<>();
        String[] ocrResults = ocrResult.split("\n");
        for (int i = 0; i < ocrResults.length; i++) {
            if (i == 0) {
                unionMemberCount.add(Integer.parseInt(ocrResults[i].split("/")[0]));
            } else {
                if (StringUtils.isNotBlank(ocrResults[i])) {
                    unionMemberCount.add(Integer.parseInt(ocrResults[i]));
                }
            }
        }
        return unionMemberCount;
    }

    public static List<Integer> getKillCount(String ocrResult) throws Exception {
        List<Integer> killCount = new ArrayList<>();
        String[] ocrResults = ocrResult.split("\n");
        for (int i = 0; i < ocrResults.length; i++) {
            if (StringUtils.isNotBlank(ocrResults[i])) {
                killCount.add(Integer.parseInt(ocrResults[i].replaceAll("\\.|,|\\s", "")));
            }
        }
        return killCount;
    }

    /**
     * 历史最高战力、阵亡
     */
    public static Integer[] getDeath(String ocrResult) throws Exception {
        String[] ocrResults = ocrResult.split("\n");
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < ocrResults.length; i++) {
            if (StringUtils.isNotBlank(ocrResults[i])) {
                Integer d = Integer.parseInt(ocrResults[i].replaceAll("\\.|,|\\s", ""));
                result.add(d);
            }
        }
        return new Integer[]{result.get(0), result.get(3)};
    }

    //    public static void main(String[] args) {
//
//        /*
//         * IMREAD_UNCHANGED = -1 ：不进行转化，比如保存为了16位的图片，读取出来仍然为16位。
//         * IMREAD_GRAYSCALE = 0 ：进行转化为灰度图，比如保存为了16位的图片，读取出来为8位，类型为CV_8UC1。
//         * IMREAD_COLOR = 1 ：进行转化为三通道图像。
//         * IMREAD_ANYDEPTH = 2 ：如果图像深度为16位则读出为16位，32位则读出为32位，其余的转化为8位。
//         * IMREAD_ANYCOLOR = 4 ：图像以任何可能的颜色格式读取
//         * IMREAD_LOAD_GDAL = 8 ：使用GDAL驱动读取文件，GDAL(Geospatial Data Abstraction
//         * Library)是一个在X/MIT许可协议下的开源栅格空间数据转换库。它利用抽象数据模型来表达所支持的各种文件格式。
//         *	它还有一系列命令行工具来进行数据转换和处理。
//         */
//
//        Mat src = Imgcodecs.imread("C:\\Users\\Administrator\\Pictures\\1\\S10416-15402646.png");//待匹配图片
//        Imgproc.resize(src, src, new Size(src.cols() / 2, src.rows() / 2));
//
//        //图片灰度化 https://blog.csdn.net/ren365880/article/details/103869207
//        Mat gary = new Mat();
//        Imgproc.cvtColor(src, gary, Imgproc.COLOR_BGR2GRAY);
//
//        //图像边缘处理 https://blog.csdn.net/ren365880/article/details/103938232
//        Mat edges = new Mat();
//        Imgproc.Canny(gary, edges, 200, 500, 3, false);
//        //发现轮廓
//        List<MatOfPoint> list = new ArrayList<>();
//        Mat hierarchy = new Mat();
//
//        /*
//         * 查找二进制图像中的轮廓。
//         * 该函数使用CITE算法：Suzuki85从二进制图像检索轮廓。轮廓是用于形状分析以及对象检测和识别的有用工具。
//         * @param image Source，一个8位单通道图像。非零像素被视为1。零像素保持为0，因此图像被视为binary。您可以使用＃compare，＃inRange，
//         * ＃threshold，＃adaptiveThreshold，＃Canny等创建灰度或彩色以外的二进制图像。如果模式等于#RETR_CCOMP或#
//         * RETR_FLOODFILL，则输入也可以是32位的整数图像（CV_32SC1）。
//         * @param轮廓检测到的轮廓。每个轮廓都存储为点的向量。
//         * @param层次结构可选的输出向量 包含有关图像拓扑的信息。它具有与轮廓数量一样多的元素。对于每个第i个轮廓轮廓[i]，元素等级[i] [0]，等级[i] [1]，等级[i]
//         * [2]和等级[i][3]均设置为0-在相同的层次级别上，基于下一个和上一个轮廓的轮廓的索引，分别是第一个子轮廓和父轮廓。如果对于轮廓i，没有下一个，上一个，父级或嵌套的轮廓
//         * ，则hierarchy [i]的相应元素将为负。
//         * @param模式轮廓检索模式，请参见#RetrievalModes
//         * RETR_EXTERNAL = 0, 只检测最外围轮廓，包含在外围轮廓内的内围轮廓被忽略；
//         * RETR_LIST = 1,检测所有的轮廓，包括内围、外围轮廓，但是检测到的轮廓不建立等级关系，彼此之间独立，没有等级关系，这就意味着这个检索模式下不存在父轮廓或内嵌轮廓
//         * RETR_CCOMP = 2,检测所有的轮廓，但所有轮廓只建立两个等级关系，外围为顶层，若外围内的内围轮廓还包含了其他的轮廓信息，则内围内的所有轮廓均归属于顶层；
//         * RETR_TREE = 3,检测所有轮廓，所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓，内层轮廓还可以继续包含内嵌轮廓
//         * RETR_FLOODFILL = 4; 官方没定义 使用此参数需要把输入源转为CV_32SC1
//         * Mat dst = new Mat();
//         * edges.convertTo(dst,CvType.CV_32SC1);
//         * @param方法轮廓近似方法，请参阅#ContourApproximationModes轮廓是从图像ROI中提取的，然后应在整个图像上下文中进行分析。
//         * CHAIN_APPROX_NONE = 1,保存物体边界上所有连续的轮廓点到contours向量内；
//         * CHAIN_APPROX_SIMPLE = 2,仅保存轮廓的拐点信息，把所有轮廓拐点处的点保存入contours向量内，拐点与拐点之间直线段上的信息点不予保留；
//         * CHAIN_APPROX_TC89_L1 = 3,使用teh-Chinl chain 近似算法;
//         * CHAIN_APPROX_TC89_KCOS = 4; 使用teh-Chinl chain 近似算法。
//         */
//        List<MatOfPoint> scoreList = new ArrayList<>();
//        Imgproc.findContours(edges, list, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_TC89_KCOS);
//        for (int i = 0; i < list.size(); i++) {
//            for (int j = i+1; j < list.size(); j++) {
//                double score = Imgproc.matchShapes(list.get(i), list.get(j), 1, 0.0);
//                BigDecimal bigDecimal = new BigDecimal("0.025");
//                BigDecimal scoreBigDecimal = new BigDecimal(score);
//                if (scoreBigDecimal.compareTo(bigDecimal) < 0 && scoreBigDecimal.compareTo(BigDecimal.ZERO) != 0) {
//                    System.out.println(scoreBigDecimal.floatValue());
//                    scoreList.add(list.get(i));
//                }
//            }
//        }
//
//        /*
//         * 绘制轮廓轮廓或填充轮廓。
//         * 如果（{thickness}> 0），则该函数在图像中绘制轮廓轮廓；如果（{thickness}<0），则该函数填充轮廓所包围的区域。
//         * @param图像目标图像。
//         * @param轮廓所有输入轮廓。每个轮廓都存储为点向量。
//         * @param outlineIdx指示要绘制的轮廓的参数。如果为负，则绘制所有轮廓。
//         * @param color轮廓的颜色。
//         * @param thickness绘制轮廓的线的粗细。如果为负（例如，thickness =＃FILLED），则绘制轮廓内部。
//         * @param lineType线路连接。请参阅https://blog.csdn.net/ren365880/article/details/103952856
//         */
//       // Imgproc.drawContours(src, scoreList, -1, new Scalar(0, 255, 0), Imgproc.FILLED, Imgproc.LINE_AA);
//        for(MatOfPoint mop:scoreList){
//            int[] p=getCenterPoint(mop.toArray());
//            if(p[1]<120){
//                continue;
//            }
//            if(p[1]>480){
//                continue;
//            }
//            Rect rectCrop = new Rect(p[0]-25, p[1]-25, 50, 50);
//            Mat image_roi = new Mat(src, rectCrop);
//            //Imgcodecs.imwrite("C:\\Users\\Administrator\\Pictures\\1\\"+System.currentTimeMillis()+".png", image_roi);
//            Imgproc.drawMarker(src,new Point(p[0],p[1]),new Scalar(0, 255, 0));
//        }
//
//        Imgcodecs.imwrite("C:\\Users\\Administrator\\Pictures\\1\\S10416-15402646-1.png", src);
//
//    }

    public static Integer getPower(String ocrResult) throws Exception {
        return Integer.parseInt(StringUtils.substringAfterLast(ocrResult, " ").replaceAll("\\.|,|\\s", ""));
    }

    public static Integer getId(String ocrResult) throws Exception {
        return Integer.parseInt(ocrResult.split(" ")[1].replaceAll("[^0-9.]", ""));
    }

    private static String imageHandle(int[] rgbRange, File file) {
        /**
         * 定义一个RGB的数组，因为图片的RGB模式是由三个 0-255来表示的 比如白色就是(255,255,255)
         */
        int[] rgb = new int[3];
        /**
         * 用来处理图片的缓冲流
         */
        BufferedImage bi = null;
        /**
         * 用ImageIO将图片读入到缓冲中
         */
        try {
            bi = ImageIO.read(file);
        } catch (IOException e) {
            log.error("ImageIO read file error", e);
        }
        /**
         * 得到图片的长宽
         */
        int width = bi.getWidth();
        int height = bi.getHeight();
        int minx = bi.getMinX();
        int miny = bi.getMinY();
        log.info("正在处理：" + file.getName());
        /**
         * 这里是遍历图片的像素，因为要处理图片的背色，所以要把指定像素上的颜色换成目标颜色
         * 这里 是一个二层循环，遍历长和宽上的每个像素
         */
        List<Integer> xPixel = new ArrayList<>();
        List<Integer> yPixel = new ArrayList<>();
        for (int i = minx; i < width; i++) {
            for (int j = miny; j < height; j++) {
                // System.out.print(bi.getRGB(jw, ih));
                /**
                 * 得到指定像素（i,j)上的RGB值，
                 */
                int pixel = bi.getRGB(i, j);
                /**
                 * 分别进行位操作得到 r g b上的值
                 */
                rgb[0] = (pixel & 0xff0000) >> 16;
                rgb[1] = (pixel & 0xff00) >> 8;
                rgb[2] = (pixel & 0xff);
                /**
                 * 值读取黑色或白色的像素
                 */
                if (rgbRange[0] <= rgb[0] & rgb[0] <= rgbRange[1] & rgbRange[2] <= rgb[1] & rgb[1] <= rgbRange[3] && rgbRange[4] <= rgb[2] & rgb[2] <= rgbRange[5]) {
                    xPixel.add(i);
                    yPixel.add(j);
                }

            }
        }
        log.info("处理完毕：" + file.getName());
        /**
         * 将缓冲对象保存到新文件中
         */
        BufferedImage bi2 = new BufferedImage(width, height, 1);
        Graphics2D g2 = (Graphics2D) bi2.getGraphics();
        g2.setBackground(Color.WHITE);
        g2.clearRect(0, 0, width, height);
        for (int m = 0; m < xPixel.size(); m++) {
            bi2.setRGB(xPixel.get(m), yPixel.get(m), 0x000000);
        }
        String tmpFilePath = file.getParentFile() + "/tmp_" + System.currentTimeMillis() + ".png";
        FileOutputStream ops = null;
        try {
            ops = new FileOutputStream(tmpFilePath);
            ImageIO.write(bi2, "png", ops);
        } catch (IOException e) {
            log.error("ImageIO write error", e);
        } finally {
            if (ops != null) {
                try {
                    ops.flush();
                    ops.close();
                } catch (IOException e) {
                    log.error("FileOutputStream close error", e);
                }
            }
        }
        return tmpFilePath;
    }

    public static void findC() throws TesseractException {
        Tesseract tesseract = new Tesseract();
        tesseract.setTessVariable("user_defined_dpi", "300");
        tesseract.setDatapath("D:\\traineddata");
        tesseract.setLanguage("eng");

        File dir = new File("d:/upload");
        int index = 0;
        StringBuffer sb = new StringBuffer();
        for (File f : dir.listFiles()) {
            if (f.isFile()) {
                if (f.getName().startsWith("tmp")) {
                    //System.out.println(f.getName());
                    Mat src = Imgcodecs.imread("d:\\upload\\" + f.getName());
                    Mat gary = new Mat();
                    Imgproc.cvtColor(src, gary, Imgproc.COLOR_BGR2GRAY);
                    Imgproc.threshold(gary, gary, 100, 255, Imgproc.THRESH_BINARY_INV);
                    List<MatOfPoint> contours = new ArrayList<>();
                    Mat hierarchy = new Mat();
                    Imgproc.findContours(gary, contours, hierarchy,
                            Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

                    for (int i = 0; i < contours.size(); i++) {

                        Rect rect = Imgproc.boundingRect(contours.get(i));
                        int x1 = (int) rect.tl().x;
                        int y1 = (int) rect.br().y;
                        int x2 = (int) rect.tl().x + (int) rect.br().x;
                        int y2 = (int) rect.tl().y;
                        //String ss=tesseract.doOCR(new File("d:\\upload\\"+f.getName()),new Rectangle(x1,y1,x2-x1,y2-y1));
                        sb.append("#");
                        sb.append(" ");
                        sb.append(x1);
                        sb.append(" ");
                        sb.append(y1);
                        sb.append(" ");
                        sb.append(x2);
                        sb.append(" ");
                        sb.append(y2);
                        sb.append(" ");
                        sb.append(index);
                        sb.append("\r\n");
                    }
                    index++;
                }
            }

        }
        System.out.println("======================");
        System.out.println(sb.toString());
        System.out.println("======================");

    }

    public static void main(String[] args) throws Exception {
//        File dir=new File("d:/upload/cb/t");
//        for(File f:dir.listFiles()){
//            if(f.isFile()){
//                System.out.print(f.getName());
//                System.out.print("=========");
//                System.out.println(matchTemplate(f.getAbsolutePath(),"d:/upload/cb2.png"));
//            }
//
//        }
        File dir = new File("d:/upload/error");
        for (File f : dir.listFiles()) {
            if (f.isFile() && StringUtils.startsWith(f.getName(), "death")) {
                Integer[] ss = getDeath(ocrImage(whiteRgbRange, f.getAbsolutePath(), "d:\\traineddata"));
                System.out.println(ss[0]);
            }

        }
//        findC();
    }

//    public static boolean matchTemplate(String imgFilePath, String templatePath) {
//        Mat image = Imgcodecs.imread(imgFilePath, Imgcodecs.IMREAD_COLOR);
//        Mat template = Imgcodecs.imread(templatePath, Imgcodecs.IMREAD_COLOR);
//        int resultRows = image.rows() - template.rows() + 1;
//        int resultCols = image.cols() - template.cols() + 1;
//        Mat result = new Mat(resultRows, resultCols, CvType.CV_32FC1);
//        Imgproc.matchTemplate(image, template, result, Imgproc.TM_CCORR_NORMED);
//        Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
//        Core.MinMaxLocResult mmlr = Core.minMaxLoc(result);
////        System.out.println(mmlr.minVal);
////        System.out.println(mmlr.maxVal == 1);
////
////        Point matchLocation = mmlr.maxLoc;
////        System.out.println(mmlr.maxVal);
////        System.out.println(matchLocation.x);
////        System.out.println(matchLocation.y);
//        return mmlr.maxVal == 1;
//    }

    /**
     * 对字节数组字符串进行Base64解码并生成图片
     */
    public static boolean generateImage(String imgStr, String imgFilePath) {
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // Base64解码
            byte[] bytes = decoder.decodeBuffer(imgStr);
            for (int i = 0; i < bytes.length; ++i) {
                // 调整异常数据
                if (bytes[i] < 0) {
                    bytes[i] += 256;
                }
            }
            // 生成jpeg图片
            OutputStream out = new FileOutputStream(imgFilePath);
            out.write(bytes);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private static int[] getCenterPoint(Point[] p) {
        double area = 0.0;//多边形面积
        double cx = 0.0, cy = 0.0;// 重心的x、y
        for (int i = 1; i <= p.length; i++) {
            double iLat = p[i % p.length].x;
            double iLng = p[i % p.length].y;
            double nextLat = p[i - 1].x;
            double nextLng = p[i - 1].y;
            double temp = (iLat * nextLng - iLng * nextLat) / 2.0;
            area += temp;
            cx += temp * (iLat + nextLat) / 3.0;
            cy += temp * (iLng + nextLng) / 3.0;
        }
        return new int[]{(int) (cx / area), (int) (cy / area)};
    }
}
